﻿using AutojsController.Config;
using AutojsController.Helpers;
using AutojsController.Services;
using Avalonia.Controls;
using Avalonia.Controls.Notifications;
using Avalonia.Threading;
using Microsoft.EntityFrameworkCore.Diagnostics.Internal;
using ReactiveUI;
using Serilog;
using SukiUI.Dialogs;
using SukiUI.Toasts;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Net;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Input;

namespace AutojsController.ViewModels
{
    public class ScanMobilesDialogViewModel: ReactiveObject
    {
        private readonly ISukiDialog dialog;
        private readonly MobilesService mobService;
        private readonly ISukiToastManager toastManager;
        public ICommand CloseDialogCommand => ReactiveCommand.Create(closeDialog);

        public ICommand ScanCommand => ReactiveCommand.CreateFromTask(scan);

        private bool _scaning = false;

        public bool Scaning
        {
            get => _scaning;
            set
            {
                this.RaiseAndSetIfChanged(ref _scaning, value);
                this.RaisePropertyChanged(nameof(ScanButtonText));
            }
        }

        public string ScanButtonText
        {
            get => _scaning ? "扫描中" : "扫描";
        }

        private string? _startIP;
        public string? StartIP
        {
            get => _startIP;
            set => this.RaiseAndSetIfChanged(ref _startIP, value);
        }

        private string? _endIP;
        public string? EndIP
        {
            get => _endIP;
            set => this.RaiseAndSetIfChanged(ref _endIP, value);
        }

        private int _scanedCount = 0;
        private readonly ILogger logger = LoggerFactory.GetLogger();

        public ScanMobilesDialogViewModel(ISukiDialog dialog, ISukiToastManager toast, MobilesService mob)
        {
            this.dialog = dialog;
            this.toastManager = toast;
            this.mobService = mob;
        }

        private void closeDialog()
        {
            this.Scaning = false;
            dialog.Dismiss();
        }

        private async Task scan()
        {
            this.Scaning = true;
            var success = await doScan();
            this.Scaning = false;
            if (success)
                dialog.Dismiss();
        }

        private void showToastWithScanProgress()
        {
            var textBlock = new TextBlock() { Text = "扫描中..." };
            var toast = toastManager.CreateToast()
            .WithTitle("扫描中...")
            .WithContent(textBlock)
            .Queue();
            
            var timer = new System.Timers.Timer(200); //设置太长,可能比dialog晚关闭
            timer.Elapsed += (_, _) =>
            {
                Dispatcher.UIThread.Invoke(() =>
                {
                    textBlock.Text = $"已扫描{_scanedCount}个IP";
                    if (!this.Scaning)
                    {
                        toastManager.Dismiss(toast);
                        timer.Stop();
                        timer.Dispose();
                    }
                });
            };
            timer.AutoReset = true;
            timer.Start();
        }

        private async Task<bool> doScan()
        {
            var _start = IPAddress.Parse(StartIP!);
            var _end = IPAddress.Parse(EndIP!);
            if (Converter.IPAddressToUint(_start) > Converter.IPAddressToUint(_end))
            {
                SukiToast.Error(toastManager, "起始IP不能大于结束IP");
                return false;
            }
            _scanedCount = 0;
            logger.Information($"扫描IP段{StartIP}-{EndIP}");
            showToastWithScanProgress();
            await scanInParallelAsync(getIPRange(_start, _end));
            return true;
        }

        private IEnumerable<IPAddress> getIPRange(IPAddress start, IPAddress end)
        {
            uint s = Converter.IPAddressToUint(start);
            uint e = Converter.IPAddressToUint(end);
            for (uint i = s; i <= e; i++)
            {
                yield return Converter.UintToIPAddress(i);
            }
        }

        /// <summary>
        /// 并行扫描, 且支持达到设备上限后中止
        /// TODO: 目前MobileConnectionService发送内容有问题, 正确添加逻辑在指定IP添加手机中, 目前使用该方法可以绕过通讯失败的错误
        /// </summary>
        private async Task scanInParallelAsync(IEnumerable<IPAddress> ipRange)
        {
            var ct = new CancellationTokenSource();
            var options = new ParallelOptions()
            {
                MaxDegreeOfParallelism = Consts.PARALLEL_CONCURRENT_COUNT,
                CancellationToken = ct.Token
            };

            try
            {
                await Parallel.ForEachAsync(ipRange, options, async (ip, token) =>
                {
                    if (token.IsCancellationRequested) return;
                    if (!this.Scaning) ct.Cancel();

                    var mc = await MobileConnectionService.CreateAndConnect(ip.ToString());
                    if (mc == null)
                    {
                        //showToast($"{ip}连接失败", NotificationType.Error); 不要在线程中直接调用
                        logger.Warning($"设备{ip}未响应");
                        Dispatcher.UIThread.Invoke(() =>
                        {
                            if (this.Scaning) SukiToast.Error(toastManager, $"{ip}连接失败");
                        });
                        Interlocked.Increment(ref _scanedCount);
                        return;
                    }
                    try
                    {
                        await mc.sendHello();
                        await mobService.AddMobileAsync(ip.ToString(), null, null);
                        var addedMobile = mobService.MobileItems.FirstOrDefault(m => m.Mobile.Address == ip.ToString());
                        logger.Information($"设备{ip}添加成功");
                        if (addedMobile !=null) addedMobile.connection = mc;
                    }
                    catch (MobileCountLimitExceededException ex)
                    {
                        logger.Error(ex, $"设备{ip}添加失败");
                        Dispatcher.UIThread.Invoke(() =>
                        {
                            SukiToast.Error(toastManager, ex.Message);
                        });
                        this.Scaning = false;
                        ct.Cancel();
                    }
                    catch(Exception ex)
                    {
                        logger.Error(ex, $"设备{ip}添加失败");
                        Dispatcher.UIThread.Invoke(() =>
                        {
                            SukiToast.Error(toastManager, $"{ip}连接失败");
                        });
                    }
                    Interlocked.Increment(ref _scanedCount);
                });
            }
            catch (OperationCanceledException)
            {
                // 不做处理
            }
            catch (Exception ex)
            {
                logger.Error(ex, $"扫描过程中出现错误");
                Dispatcher.UIThread.Invoke(() =>
                {
                    SukiToast.Error(toastManager, "扫描过程中出现错误");
                });
            }
        }
    }
}
